﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Tuxedo.Compile
{
	internal class FPClass : IRootEntity
	{
		public string FullyQualifiedName { get; private set; }
		public Visibility Visbility { get; private set; }
		public string[] Templates { get; private set; }
		public UnverifiedType[] Subclasses { get; private set; }
		public IFPClassMember[] Members { get; private set; }
		public bool IsStatic { get; private set; }
		public bool IsAbstract { get; private set; }
		public bool IsSealed { get; private set; }
		public Dictionary<string, FieldDefinition> FieldsByName { get; private set; }


		public FPClass(Visibility visibility, string namespacePrefix, UnverifiedType name, IList<UnverifiedType> subclasses, IList<IFPClassMember> members, bool isStatic, bool isAbstract, bool isSealed)
		{
			this.Visbility = visibility;
			this.IsStatic = isStatic;
			this.IsAbstract = isAbstract;
			this.IsSealed = isSealed;

			this.FullyQualifiedName = (namespacePrefix == null || namespacePrefix.Length == 0) ? name.Name : (namespacePrefix + "." + name.Name);
			List<string> templates = new List<string>();
			foreach (UnverifiedType template in name.Templates)
			{
				templates.Add(template.Name);
			}
			this.Templates = templates.ToArray();
			this.Subclasses = subclasses.ToArray();
			this.Members = members.ToArray();

			Dictionary<string, FieldDefinition> fieldsByName = new Dictionary<string, FieldDefinition>();
			foreach (IFPClassMember member in members)
			{
				FieldDefinition fd = member as FieldDefinition;
				if (fd != null)
				{
					fieldsByName[fd.Name] = fd;
				}
			}

			this.FieldsByName = fieldsByName;
		}

		private const int CLASS_TYPE = 1;
		private const int INTERFACE_TYPE = 2;
		private const int ENUM_TYPE = 3;
		private const int STRUCT_TYPE = 4;

		// TODO: base class for FPClass, FPEnum, FPInterface, and FPStruct
		public static FPClass ParseClass(string namespacePrefix, TokenStream tokens)
		{
			// { public | private | protected | internal }? protected only valid in nested classes
			// { static | abstract | sealed }? class-only
			// { class | interface | enum }

			Visibility visibility = Visibility.Internal;
			if (tokens.PopIfPresent("public"))
				visibility = Visibility.Public;
			else if (tokens.PopIfPresent("private"))
				visibility = Visibility.Private;
			else if (tokens.PopIfPresent("protected"))
				visibility = Visibility.Protected;
			else if (tokens.PopIfPresent("internal"))
				visibility = Visibility.Internal;

			bool isStatic = false;
			bool isAbstract = false;
			bool isSealed = false;

			if (tokens.PopIfPresent("static"))
				isStatic = true;
			else if (tokens.PopIfPresent("abstract"))
				isStatic = true;
			else if (tokens.PopIfPresent("isSealed"))
				isSealed = true;

			int type;
			if (tokens.PopIfPresent("class"))
			{
				type = CLASS_TYPE;
			}
			else if (tokens.PopIfPresent("interface"))
			{
				type = INTERFACE_TYPE;
			}
			else if (tokens.PopIfPresent("enum"))
			{
				type = ENUM_TYPE;
				throw new Exception("Enums are not supported yet");
			}
			else if (tokens.PopIfPresent("struct"))
			{
				type = STRUCT_TYPE;
				throw new Exception("Structs are not supported yet.");
			}
			else
			{
				throw new Exception("Expected: \"class\", \"interface\", \"struct\", or \"enum\"");
			}

			if (type == CLASS_TYPE)
			{
				UnverifiedType name = UnverifiedType.Parse(tokens, false);
				if (name.Name.Contains('.'))
				{
					throw new Exception("Invalid class name");
				}
				foreach (UnverifiedType template in name.Templates)
				{
					// TODO: is this right?
					if (template.Name.Length != 1 || template.Templates.Length > 0)
					{
						throw new Exception("Invalid template for class");
					}
				}

				List<UnverifiedType> subclasses = new List<UnverifiedType>();
				if (tokens.PopIfPresent(":"))
				{
					subclasses.Add(UnverifiedType.Parse(tokens, false));

					while (tokens.PopIfPresent(","))
					{
						subclasses.Add(UnverifiedType.Parse(tokens, false));
					}
				}

				tokens.PopExpected("{");

				List<IFPClassMember> members = new List<IFPClassMember>();

				string prefix = name.Name;
				if (namespacePrefix != null && namespacePrefix.Length > 0)
				{
					prefix = namespacePrefix + "." + prefix;
				}

				while (!tokens.PopIfPresent("}"))
				{
					members.Add(IFPClassMember.ParseMember(prefix + name.Name, name.Name, tokens));
				}

				return new FPClass(visibility, namespacePrefix, name, subclasses, members, isStatic, isAbstract, isSealed);
			}
			else
			{

			}
			tokens.PopExpected("{");

			tokens.PopExpected("}");

			return null;
		}
	}
}
